/**
Contains implementation for UDP Client 
*/

#include "unp.h"
#include "unpifiplus.h"

int main ()
{

	char IPclient [INET_ADDRSTRLEN]; 
	char IPserver [INET_ADDRSTRLEN];
	char filename [100]="\0"; 
	char buf [100]="\0";
	char * ptr;
	
	int SERVER_PORT;
	int Win;
	int seed;
	int mu;
	int i=1;
	int j;
	int sockfd;
	int flags;
	
	float p;
	
	FILE * fp;
	
	struct in_addr ipv4_addr;

	struct ifi_info	*ifi;
	struct ifi_info *ifihead;	

	struct sockaddr * sa;
	struct sockaddr_in cliaddr;
	struct sockaddr_in servaddr;
	struct sockaddr_in sock_nm;	
	
	socklen_t len;	
	
	if ( ( fp = fopen ("client.in","r" ) ) == NULL )
	{
		printf( "client.in not found\n" );
		exit( 1 );
	}
	else
	{
		while ( Fgets ( buf, 100, fp ) != NULL )
		{
			flags=0;

			if( i != 1 && i != 3 )
			{
				for( j = 0; buf[j] != '\0'; j++ )
				{
					if( isdigit ( buf[j] ) )
					{
						continue;
					}	
					else if( flags == -1 )
					{
						break;
					}	
					else if( i==6 && buf[j] == '.' )
					{
						flags = -1;					
					}
					else 
					{
						break;	
					}
				}
				if( j != strlen( buf )-1 )
					flags = 1;
			}

			switch(i)
			{
				case 1: 
					strcpy( IPserver, buf );
					IPserver[ strlen(buf) - 1 ] = '\0';
					if( inet_pton ( AF_INET, IPserver, &ipv4_addr.s_addr ) <= 0 )
					{
						printf( "Error server.in :: Line 1 :: contains invalid Server IP\n" );
						exit( 1 );
					}	
					i++;
					break;
				case 2:
					if( flags || ( SERVER_PORT = atoi(buf) ) == 0 )
					{
						printf( "Error server.in :: Line 2 :: Invalid Port number\n" );
						exit( 1 );
					}	
					i++;
					break;
				case 3:
					strcpy( filename, buf );
					filename[ strlen(buf)-1 ] = '\0';
					i++;
					break;	
				case 4:
					if( flags || ( Win=atoi(buf) ) == 0 )
					{
						printf( "Error server.in :: Line 4 :: Invalid Receiving Sliding Window Size\n" );
						exit( 1 );
					}	
					i++;
					break;	
				case 5:
					if( flags || ( seed = atoi(buf) ) == 0 )
					{
						printf( "Error server.in :: Line 5 :: Invalid Seed value\n" );
						exit( 1 );
					}	
					i++;					
					break;	
				case 6:
					p=atof( buf );
					if( flags == 1 || p < 0.0 || p > 1.0 )
					{
						printf( "Error server.in :: Line 6 :: Invalid Probability value\n" );
						exit( 1 );
					}	
					i++;									
					break;	
				case 7:
					if( flags || ( mu = atoi(buf) ) == 0 )
					{
						printf( "Error server.in :: Line 7 :: Invalid Mean value\n" );
						exit( 1 );
					}	
					i++;								
					break;	
					
			}int flags = 0;	
			
			for( j = 0; j < 100; j++ )
			{
				buf[j] = '\0';
			}	
			
		}
		
		fclose(fp);
		
	}
	
	flags=3;
	
	for ( ifihead = ifi = Get_ifi_info_plus( AF_INET, 1 ), i = 0; ifi != NULL; ifi = ifi->ifi_next, i++) 
	{
		char ip_addr[INET_ADDRSTRLEN]="\0";
		char net_msk[INET_ADDRSTRLEN]="\0";
		char sub_msk[INET_ADDRSTRLEN]="\0";
		
		int p1; 
		int p2;
		
		sa = ifi->ifi_addr;
		strcpy( ip_addr, Sock_ntop_host( sa, sizeof( *sa ) ) );
		sa =  ifi->ifi_ntmaddr;
		strcpy( net_msk, Sock_ntop_host( sa, sizeof( *sa ) ) );

//
//	The for loop below finds the subnet mask for each interface using the IP address and the network mask
//

		for( p1 = 0, p2 = 0, j = 0; j < 4; j++, p1++, p2++ ) 
		{
			int a;
			int b; 
			int k;
			
			char s1[4] = "\0";
			char s2[4] = "\0";
			
			for( k = 0; ip_addr[p1] != '.' && k < 4; k++ )
			{
				s1[k] = ip_addr[p1++];
			}	
			
			for( k = 0; net_msk[p2] != '.' && k < 4; k++ )
			{
				s2[k] = net_msk[p2++];
			}

			a=atoi(s1);
			b=atoi(s2);	
			sprintf(s1,"%d%s",a&b,"\0");

			if(j)
			{
				strcat( sub_msk, "." );	
			}
			
			strcat( sub_msk, s1 );			
		}

		printf( "\nClient Interface %d :-\nIP Address :: %s\nNetwork Mask :: %s\nSubnet Mask :: %s\n\n", (i+1), ip_addr, net_msk, sub_msk );

		if( strcmp( IPserver, ip_addr ) == 0 )
		{
//
//	Client and Server on the same host		
//			
			strcpy( IPserver, "127.0.0.1" );
			strcpy( IPclient, ip_addr );
			printf( "Client and Server are on the same host\nIPclient :: %s IPserver :: %s \n", IPclient, IPserver );
			flags = 1;
			break;
		}
		else 
		{
//
//	Check if Server is local to Client i.e. if Client and Server share the same subnet mask
//				
			char check_sub_msk[INET_ADDRSTRLEN]="\0";
			
			for( p1 = 0, p2 = 0, j = 0; j < 4; j++, p1++ , p2++ ) 
			{
				int a;
				int b; 
				int k;
			
				char s1[4] = "\0";
				char s2[4] = "\0";

				for( k = 0; IPserver[p1] != '.' && k < 4; k++ )
				{
					s1[k] = IPserver[p1++];
				}

				for( k = 0; net_msk[p2] != '.' && k<4; k++ )
				{
					s2[k] = net_msk[p2++];
				}

				a=atoi(s1);
				b=atoi(s2);	
				sprintf(s1,"%d%s",a&b,"\0");
				
				if(j)
				{
					strcat( check_sub_msk, "." );	
				}
				
				strcat( check_sub_msk, s1 );			
			}

			if( strcmp( check_sub_msk, sub_msk ) == 0 )
			{
				strcpy( IPclient, ip_addr );
				printf("Client and Server are local i.e. in the same subnet\nIPclient :: %s IPserver :: %s\n", IPclient, IPserver);
				flags=2;
				break;
			}
		}
		if(ifi->ifi_next == NULL )		
		{
//
// Client and Server are neither local nor on the same host
//		
			strcpy( IPclient, ip_addr );
			printf( "Client and Server are not local\nIPclient :: %s IPserver :: %s \n", IPclient, IPserver );
		}
	}

	free_ifi_info_plus(ifihead);

//
// Creating a UDP socket and Binding with the Client's IP and a ephemeral port chosen by the Kernel
//

	sockfd=Socket( AF_INET, SOCK_DGRAM, 0 );
	bzero( &cliaddr, sizeof( cliaddr ) );
	cliaddr.sin_family = AF_INET;
	inet_pton( AF_INET, IPclient, &cliaddr.sin_addr );
	cliaddr.sin_port = htons( 0 );
	Bind( sockfd, ( SA * ) &cliaddr, sizeof( cliaddr ) );
	
	len = sizeof( sock_nm );
	getsockname( sockfd, ( SA * ) &sock_nm, &len );

	printf("\nClient Socket created and Binded\nIPClient:Portno :- %s \n", Sock_ntop( ( SA * ) &sock_nm, len ) );

//
// Connecting the socket by specifying Server's IP and well known port using UDP Connect
//
	
	bzero( &servaddr, sizeof( servaddr ) );
	servaddr.sin_family = AF_INET;
	inet_pton( AF_INET, IPserver, &servaddr.sin_addr );
	servaddr.sin_port = htons( SERVER_PORT );
	Connect( sockfd, ( SA * ) &servaddr, sizeof( servaddr ) );
	
	bzero( &sock_nm, sizeof( sock_nm ) );
	len = sizeof( sock_nm );
	getpeername( sockfd, ( SA * ) &sock_nm, &len );
	printf("\nAfter UDP Connect \nIPServer:Portno :- %s \n", Sock_ntop( ( SA * ) &sock_nm, len ) );
	
	if( flags == 1 )
	{
//
//	If Client and Server are on the same host then flags is set to MSG_DONTROUTE in send function
//	
		flags = MSG_DONTROUTE;
	}	
	else
	{
		flags = 0;	
	}	
	
	send( sockfd, filename, 100, flags );
	printf( "Request sent for %s\n", filename );
	
	for(i = 0 ; i < 100; i++ )
	{
		buf[i]='\0';
	}
		
	recv( sockfd, buf, 100, flags);
	printf( "Buf content %s\n", buf );		
	ptr=strchr( buf, ':' );
	strcpy( buf, ( ptr+1 ) );
	printf( "Buf content %s\n", buf );	

//
// Client Reconnects to Server on the new Server port (the ephemeral port)
//

	servaddr.sin_port = htons( atoi( buf ) );
	Connect( sockfd, (SA*) &servaddr, sizeof(servaddr) );
	printf( "New connection established with Server at Port no %s\n", buf );
	strcpy( buf, "ACK\0" );
	send( sockfd, buf, 100, flags );
	printf( "ACK sent to Server\n" );
	
	while( 1 )
	{
	
//
// File Transfer operation
//	

		recv( sockfd, buf, 100, flags );
		
		if( buf[0] == EOF )
		{
			break;
		}	
		else
		{
			printf( "%s", buf );	
		}	
		
	}
	
	close( sockfd );
	
	return( 0 );
}
